package org.xserver.component.extension.filter.parameter;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.xserver.common.util.CharsetUtil;
import org.xserver.common.util.JsonEngine;
import org.xserver.component.core.interfaces.InterfaceContext;
import org.xserver.component.core.interfaces.InterfaceMeta;
import org.xserver.component.core.XServerHttpContextAttachment;
import org.xserver.component.core.XServerHttpRequest;
import org.xserver.component.exception.FilterProcessError;
import org.xserver.component.extension.filter.AbstractContextFilter;
import org.xserver.component.extension.filter.parameter.util.FilterUtil;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * The filter is wrapping parameters before into business logic process, filter
 * support {@code GET} and {@code POST} HTTP methods parameters reflect into a
 * javaBean(javaBean's field).
 * 
 * <h3>Event timeline</h3>
 * 
 * <ul>
 * <li>2015/08/10 support basic data type, inner type and one level bean type.</li>
 * <li>2015/10/20 replace one and more level bean type by json style request
 * parameters to javaBean(use jackson lib to reflect to javaBean).</li>
 * <li>2016/06/20 support
 * <code> HTTP post with application/json body data.</code></li>
 * </ul>
 * 
 * 
 * @author idoldefine
 * @since 2015/08/10
 * 
 */
public class XServerParametersProcessFilter extends AbstractContextFilter {
	private static final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

	private static Map<String, List<JavaType>> javaTypeInfos = new HashMap<String, List<JavaType>>();

	@Override
	public void contextInitialized() throws FilterProcessError {
		Map<String, InterfaceMeta> interfaceContextInfos = getInterfaceContext()
				.getInterfaceContext();

		TypeFactory typeFactory = JsonEngine.DEFAULT_JACKSON_MAPPER
				.getTypeFactory();
		for (Entry<String, InterfaceMeta> entry : interfaceContextInfos
				.entrySet()) {
			String path = entry.getKey();
			InterfaceMeta meta = entry.getValue();
			Method method = meta.getMethod();
			Type[] types = method.getGenericParameterTypes();
			List<JavaType> javaTypes = new ArrayList<JavaType>(types.length);

			for (Type type : types) {
				String canonical = type.toString().replaceAll("class ", "")
						.replaceAll("interface ", "").replaceAll(" ", "");
				JavaType javaType = typeFactory
						.constructFromCanonical(canonical);
				javaTypes.add(javaType);
			}
			javaTypeInfos.put(path, javaTypes);
		}
	}

	/**
	 * reflect request parameters to interface's arguments
	 */
	@Override
	public void process(XServerHttpContextAttachment attachment)
			throws FilterProcessError {
		try {
			InterfaceContext context = getInterfaceContext();

			XServerHttpRequest request = attachment.getRequest();
			
			InterfaceMeta meta = context.getInterfaceMeta(request.getPath());

			Method method = meta.getMethod();

			String[] keys = discoverer.getParameterNames(method);

			List<JavaType> javaTypes = javaTypeInfos.get(request.getPath());

			List<Object> parameterValues = FilterUtil.getParameterValues(
					attachment, keys, javaTypes);

			attachment.attachment(
					XServerParametersProcessFilter.class.getSimpleName(),
					parameterValues);
		} catch (Exception e) {
			throw new FilterProcessError(
					XServerParametersProcessFilter.class.getSimpleName(),
					e.getMessage());
		}
	}
}
